﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;
using Microsoft.VisualStudio.TextTemplating;
using System.IO;
using System.Net.Mail;
using System.Net;
using System.Diagnostics;
using System.CodeDom.Compiler;
using Kalman.Data.SchemaObject;
using Kalman.Utilities;
using Kalman.Extensions;
using Kalman.Studio.T4TemplateEngineHost;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor;

namespace Kalman.Studio
{
    public partial class BatchBuildCode : DockableForm
    {
        private string _CodeType = CodeType.CSHARP;
        private SODatabase currentDatabase;
        private List<SOTable> tableList = new List<SOTable>();
        private List<SOView> viewList = new List<SOView>();
        private Dictionary<string, string> diTemplateFiles = new Dictionary<string, string>();

        private List<string> templateFileNames = new List<string>();

        public BatchBuildCode(SODatabase db)
        {
            InitializeComponent();
            currentDatabase = db;
            tableList = db.TableList;
            viewList = db.ViewList;
        }

        private void BatchBuildCode_Load(object sender, EventArgs e)
        {
            gbTableSelect.Text = string.Format("当前数据库[{0}]", currentDatabase);

            foreach (SOTable t in tableList)
            {
                listBox1.Items.Add(t);
            }
            foreach (SOView t in viewList)
            {
                listBox1.Items.Add(t);
            }
            folderBrowserDialog1.RootFolder = Environment.SpecialFolder.MyComputer;
            txtOutputPath.Text = Path.Combine(Application.StartupPath, "Output");
            txtNameSpace.Text = nameSpace;

            SetDocumentCodeType(textEditorControl1, CodeType.CSHARP);
            LoadTemplateTree();
        }

        //选择代码输出目录
        private void btnBrowser_Click(object sender, EventArgs e)
        {
            DialogResult result = folderBrowserDialog1.ShowDialog();
            if (result == DialogResult.OK)
            {
                txtOutputPath.Text = folderBrowserDialog1.SelectedPath;
            }
        }

        //处理窗体关闭事件
        protected override void OnClosing(CancelEventArgs e)
        {
            if (backgroundWorker1.IsBusy == false)
            {
                base.OnClosing(e);
            }
            else
            {
                e.Cancel = true;
                MsgBox.Show("正在生成代码，请不要关闭窗口");
            }
        }

        #region 模板树相关代码

        private void LoadTemplateTree()
        {
            string templatePath = Path.Combine(Application.StartupPath, "T4Template");

            TreeNode root = new TreeNode("T4Templates", 0, 0);
            root.ContextMenuStrip = cmsTree;
            tvTemplate.Nodes.Add(root);

            DirectoryInfo dirInfo = new DirectoryInfo(templatePath);
            ExpentTemplateDir(dirInfo, root);
        }

        //展开模板文件夹
        private void ExpentTemplateDir(DirectoryInfo rootDirInfo, TreeNode root)
        {
            DirectoryInfo[] dirs = rootDirInfo.GetDirectories();
            foreach (DirectoryInfo dir in dirs)
            {
                TreeNode node = new TreeNode(dir.Name, 1, 1);
                node.Tag = dir;
                node.Name = dir.Name;
                if (root.Nodes.ContainsKey(node.Name) == false) root.Nodes.Add(node);
            }

            FileInfo[] files = rootDirInfo.GetFiles();
            foreach (FileInfo file in files)
            {
                TreeNode node = new TreeNode(file.Name, 2, 2);
                node.Tag = file;
                node.Name = file.Name;
                if (root.Nodes.ContainsKey(node.Name) == false) root.Nodes.Add(node);
            }

            root.Expand();
        }

        private void tvTemplate_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                TreeView tv = sender as TreeView;
                TreeNode tn = tv.GetNodeAt(e.X, e.Y);
                tv.SelectedNode = tn;
            }
        }

        private void tvTemplate_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            TreeView tv = sender as TreeView;
            TreeNode tn = tv.GetNodeAt(e.X, e.Y);

            if (tn.Tag is DirectoryInfo)
            {
                DirectoryInfo dir = (DirectoryInfo)tn.Tag;
                if (Directory.Exists(dir.FullName) == false)
                {
                    MsgBox.Show("目标可能被删除、移动、改名，请刷新模板树");
                    return;
                }
                ExpentTemplateDir(dir, tn);
            }
            if (tn.Tag is FileInfo)
            {
                FileInfo fi = tn.Tag as FileInfo;
                if (File.Exists(fi.FullName) == false)
                {
                    MsgBox.Show("目标可能被删除、移动、改名，请刷新模板树");
                    return;
                }
                textEditorControl1.LoadFile(fi.FullName);
                gbTemplateFile.Text = fi.FullName;
            }
            SetDocumentCodeType(textEditorControl1, CodeType.CSHARP);
        }

        private void SetDocumentCodeType(TextEditorControl editor, string codeType)
        {
            IHighlightingStrategy strategy = HighlightingStrategyFactory.CreateHighlightingStrategy(codeType);
            editor.Document.HighlightingStrategy = strategy;
            _CodeType = codeType;
        }

        //右键菜单项命令：刷新模板树
        private void menuItemRefresh_Click(object sender, EventArgs e)
        {
            tvTemplate.Nodes.Clear();
            LoadTemplateTree();
        }

        //右键菜单项命令：模板管理
        private void menuItemManage_Click(object sender, EventArgs e)
        {
            base.MainForm.ShowTemplateExplorer();
        }

        #endregion

        #region 处理编辑命令

        private void menuItemUndo_Click(object sender, EventArgs e)
        {
            Undo(textEditorControl1);
        }

        private void menuItemRedo_Click(object sender, EventArgs e)
        {
            Redo(textEditorControl1);
        }

        private void menuItemCut_Click(object sender, EventArgs e)
        {
            Cut(sender, e);
        }

        private void menuItemCopy_Click(object sender, EventArgs e)
        {
            Copy(sender, e);
        }

        private void menuItemPaste_Click(object sender, EventArgs e)
        {
            Paste(sender, e);
        }

        private void menuItemDelete_Click(object sender, EventArgs e)
        {
            Delete(sender, e);
        }

        private void menuItemSelectAll_Click(object sender, EventArgs e)
        {
            SelectAll(sender, e);
        }

        public void Undo(TextEditorControl editor)
        {
            editor.Undo();
        }

        public void Redo(TextEditorControl editor)
        {
            editor.Redo();
        }

        public void Cut(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.Cut(sender, e);
        }

        public void Copy(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.Copy(sender, e);
        }

        public void Paste(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.Paste(sender, e);
        }

        public void Delete(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.Delete(sender, e);
        }

        public void SelectAll(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.SelectAll(sender, e);
        }

        #endregion

        private void menuItemSave_Click(object sender, EventArgs e)
        {
            textEditorControl1.SaveFile(gbTemplateFile.Text);
        }

        #region 列表选择相关

        private void listBox1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            SelectOne();
        }

        private void listBox2_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            RemoveOne();
        }

        private void btnSelectAll_Click(object sender, EventArgs e)
        {
            SelectAll();
        }

        private void btnSelectOne_Click(object sender, EventArgs e)
        {
            SelectOne();
        }

        private void btnRemoveOne_Click(object sender, EventArgs e)
        {
            RemoveOne();
        }

        private void btnRemoveAll_Click(object sender, EventArgs e)
        {
            RemoveAll();
        }

        private void SelectAll()
        {
            if (backgroundWorker1.IsBusy) return;
            if (listBox1.Items.Count > 0)
            {
                listBoxTables.Items.AddRange(listBox1.Items);
                listBox1.Items.Clear();
            }
        }

        private void SelectOne()
        {
            if (backgroundWorker1.IsBusy) return;
            object[] items = new object[listBox1.SelectedItems.Count];
            listBox1.SelectedItems.CopyTo(items, 0);
            listBoxTables.Items.AddRange(items);

            foreach (var item in items)
            {
                listBox1.Items.Remove(item);
            }
        }

        private void RemoveOne()
        {
            if (backgroundWorker1.IsBusy) return;
            object[] items = new object[listBoxTables.SelectedItems.Count];
            listBoxTables.SelectedItems.CopyTo(items, 0);
            listBox1.Items.AddRange(items);

            foreach (var item in items)
            {
                listBoxTables.Items.Remove(item);
            }
        }

        private void RemoveAll()
        {
            if (backgroundWorker1.IsBusy) return;
            if (listBoxTables.Items.Count > 0)
            {
                listBox1.Items.AddRange(listBoxTables.Items);
                listBoxTables.Items.Clear();
            }
        }

        #endregion

        #region 代码生成相关

        private string nameSpace = "HW.TPM";
        private string tablePrefix = string.Empty;
        private int prefixLevel = 1;
        private string templateFile = string.Empty;
        private string outputPath = string.Empty;

        private void btnBuildCode_Click(object sender, EventArgs e)
        {
            diTemplateFiles.Clear();
            templateFileNames.Clear();
            RecursiveTree(this.tvTemplate);

            if (templateFileNames.Count == 0)
            {
                templateFile = gbTemplateFile.Text;
                if (!File.Exists(templateFile)) return;
                textEditorControl1.SaveFile(templateFile);
                //  arr.Add(templateFile);
            }

            if (listBoxTables.Items.Count == 0) return;

            outputPath = txtOutputPath.Text;
            if (txtNameSpace.Text.Trim() != "") nameSpace = txtNameSpace.Text.Trim();

            if (cbDeleteTablePrifix.Checked && txtTablePrefix.Text.Trim().Length > 0) tablePrefix = txtTablePrefix.Text.Trim();
            prefixLevel = ConvertUtil.ToInt32(txtPrefixLevel.Text, 1);

            btnBuildCode.Enabled = false;
            backgroundWorker1.RunWorkerAsync();
        }

        private void DoBuild()
        {
            int finish = 0;
            int total = listBoxTables.Items.Count;

            //遍历选中的表，一张表对应生成一个代码文件
            foreach (object item in listBoxTables.Items)
            {
                if (item is SOTable)
                {
                    DoBuildTable(item);
                    (item as SOTable).ColumnList = null;
                    (item as SOTable).IndexList = null;
                    System.GC.Collect();
                }
                else if (item is SOView)
                {
                    DoBuildView(item);
                    (item as SOView).ColumnList = null;
                    System.GC.Collect();
                }
                finish = finish + 1;
                int percent = ConvertUtil.ToInt32(finish * 100 / total, 0);

                backgroundWorker1.ReportProgress(percent, item);
                System.GC.Collect();
            }//end build code foreach
            System.GC.Collect();
        }

        private void DoBuildTable(object item)
        {
            //将表和文件进行缓存，再次使用时不用再次进行IO
            List<SOColumn> columnList;
            List<SOIndex> indexList;
            SOTable table = item as SOTable;

            string className = table.Name;
            if (cbDeleteTablePrifix.Checked) className = table.Name.RemovePrefix(tablePrefix, prefixLevel).Replace(" ", "");
            if (cbClassNamePascal.Checked) className = className.InitialToUpperMulti();
            if (cbClassNameRemovePlural.Checked) className = className.EndsWith("s") ? className.TrimEnd('s') : className.Trim();
            if (cbAddSuffix.Checked) className = txtClassPrefix.Text.Trim() + className + txtClassSuffix.Text.Trim();

            if (templateFileNames.Count == 0)
            {
                templateFile = gbTemplateFile.Text;
                templateFileNames.Add(templateFile);
            }

            columnList = table.ColumnList;//可能传入的是从PDObject对象转换过来的SODatabase对象
            if (columnList == null || columnList.Count == 0)
            {
                columnList = DbSchemaHelper.Instance.CurrentSchema.GetTableColumnList(table);
            }
            indexList = table.IndexList;//可能传入的是从PDObject对象转换过来的SODatabase对象
            if (indexList == null || indexList.Count == 0)
            {
                indexList = DbSchemaHelper.Instance.CurrentSchema.GetTableIndexList(table);
            }

            //模板文件循环
            foreach (string templateFileName in templateFileNames)
            {
                string classNameItem = "";
                string nameSpaceItem = "";
                string outputPathItem = "";
                var fileName = Path.GetFileNameWithoutExtension(templateFileName);
                if (fileName.Split('、').Length > 1)
                {
                    classNameItem = string.Format(fileName.Split('、')[1], className);
                }
                else
                {
                    classNameItem = string.Format(fileName, className);
                }

                var directoryName = Path.GetFileName(Path.GetDirectoryName(templateFileName));
                nameSpaceItem = nameSpace + "." + directoryName;

                //生成代码文件

                TableHost host = new TableHost();
                host.Table = table;

                host.ColumnList = columnList;
                host.IndexList = indexList;
                host.TemplateFile = templateFileName;
                host.SetValue("NameSpace", nameSpaceItem);
                host.SetValue("ClassName", classNameItem);
                host.SetValue("TablePrefix", tablePrefix);
                //host.SetValue("ColumnPrefix", columnPrefix);
                host.SetValue("PrefixLevel", prefixLevel);

                string templateContent = string.Empty;
                if (diTemplateFiles.ContainsKey(templateFileName))
                {
                    templateContent = diTemplateFiles[templateFileName];
                }
                else
                {
                    templateContent = File.ReadAllText(templateFileName);
                    diTemplateFiles.Add(templateFileName, templateContent);
                }
                string outputContent;
                using (Engine engine = new Engine())
                {
                    outputContent = engine.ProcessTemplate(templateContent, host);
                    engine.Dispose();
                }
                outputPathItem = Path.Combine(outputPath, directoryName);
                //string outputFile = Path.Combine(outputPath, string.Format("{0}.cs", className));
                string outputFile = Path.Combine(outputPathItem, string.Format("{0}{1}", table.Name, host.FileExtention));
                if (cbClassNameIsFileName.Checked) outputFile = Path.Combine(outputPathItem, string.Format(@"{0}{1}", classNameItem, host.FileExtention));

                StringBuilder sb = new StringBuilder();
                if (host.ErrorCollection != null && host.ErrorCollection.HasErrors)
                {
                    foreach (CompilerError err in host.ErrorCollection)
                    {
                        sb.AppendLine(err.ToString());
                    }
                    outputContent = outputContent + Environment.NewLine + sb.ToString();
                    outputFile = outputFile + ".error";
                }
                sb.Clear();
                if (Directory.Exists(outputPathItem) == false) Directory.CreateDirectory(outputPathItem);
                File.WriteAllText(outputFile, outputContent, Encoding.UTF8);
                host = null;
            }
        }

        private void DoBuildView(object item)
        {
            //将表和文件进行缓存，再次使用时不用再次进行IO
            List<SOColumn> columnList;
            SOView view;

            view = item as SOView;

            string className = view.Name;
            if (cbDeleteTablePrifix.Checked) className = view.Name.RemovePrefix(tablePrefix, prefixLevel).Replace(" ", "");
            if (cbClassNamePascal.Checked) className = className.InitialToUpperMulti();
            if (cbClassNameRemovePlural.Checked) className = className.EndsWith("s") ? className.TrimEnd('s') : className.Trim();
            if (cbAddSuffix.Checked) className = txtClassPrefix.Text.Trim() + className + txtClassSuffix.Text.Trim();

            if (templateFileNames.Count == 0)
            {
                templateFile = gbTemplateFile.Text;
                templateFileNames.Add(templateFile);
            }

            columnList = view.ColumnList;//可能传入的是从PDObject对象转换过来的SODatabase对象
            if (columnList == null || columnList.Count == 0)
            {
                columnList = DbSchemaHelper.Instance.CurrentSchema.GetViewColumnList(view);
            }
            //模板文件循环
            foreach (string templateFileName in templateFileNames)
            {
                string classNameItem = "";
                string nameSpaceItem = "";
                string outputPathItem = "";
                var fileName = Path.GetFileNameWithoutExtension(templateFileName);
                if (fileName.Split('、').Length > 1)
                {
                    classNameItem = string.Format(fileName.Split('、')[1], className);
                }
                else
                {
                    classNameItem = string.Format(fileName, className);
                }

                var directoryName = Path.GetFileName(Path.GetDirectoryName(templateFileName));
                nameSpaceItem = nameSpace + "." + directoryName;

                //生成代码文件

                ViewHost host = new ViewHost();
                host.View = view;

                host.ColumnList = columnList;
                host.TemplateFile = templateFileName;
                host.SetValue("NameSpace", nameSpaceItem);
                host.SetValue("ClassName", classNameItem);
                host.SetValue("TablePrefix", tablePrefix);
                //host.SetValue("ColumnPrefix", columnPrefix);
                host.SetValue("PrefixLevel", prefixLevel);

                string templateContent = string.Empty;
                if (diTemplateFiles.ContainsKey(templateFileName))
                {
                    templateContent = diTemplateFiles[templateFileName];
                }
                else
                {
                    templateContent = File.ReadAllText(templateFileName);
                    diTemplateFiles.Add(templateFileName, templateContent);
                }
                string outputContent;
                using (Engine engine = new Engine())
                {
                    outputContent = engine.ProcessTemplate(templateContent, host);
                }
                outputPathItem = Path.Combine(outputPath, directoryName);
                //string outputFile = Path.Combine(outputPath, string.Format("{0}.cs", className));
                string outputFile = Path.Combine(outputPathItem, string.Format("{0}{1}", view.Name, host.FileExtention));
                if (cbClassNameIsFileName.Checked) outputFile = Path.Combine(outputPathItem, string.Format(@"{0}{1}", classNameItem, host.FileExtention));

                StringBuilder sb = new StringBuilder();
                if (host.ErrorCollection != null && host.ErrorCollection.HasErrors)
                {
                    foreach (CompilerError err in host.ErrorCollection)
                    {
                        sb.AppendLine(err.ToString());
                    }
                    outputContent = outputContent + Environment.NewLine + sb.ToString();
                    outputFile = outputFile + ".error";
                }

                if (Directory.Exists(outputPathItem) == false) Directory.CreateDirectory(outputPathItem);
                File.WriteAllText(outputFile, outputContent, Encoding.UTF8);
            }
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            DoBuild();
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
            SOTable table = e.UserState as SOTable;

            if (e.ProgressPercentage == 100)
            {
                lblMsg.Text = "代码已全部生成";
            }
            else
            {
                lblMsg.Text = string.Format("已完成：{0}%，正在处理：{1}", e.ProgressPercentage, table.Name);
            }
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            DialogResult result = MsgBox.ShowQuestionMessage("代码生成成功，是否打开输出目录", "代码生成消息提示");
            if (result == DialogResult.Yes)
            {
                Process p = new Process();
                p.StartInfo = new ProcessStartInfo(outputPath);
                p.Start();
            }
            btnBuildCode.Enabled = true;
        }

        #endregion

        private void tvTemplate_AfterCheck(object sender, TreeViewEventArgs e)
        {
            try
            {
                if (e.Node.Nodes.Count > 0)
                {
                    bool NoFalse = true;
                    foreach (TreeNode tn in e.Node.Nodes)
                    {
                        if (tn.Checked == false)
                        {
                            NoFalse = false;
                        }
                    }
                    if (e.Node.Checked == true || NoFalse)
                    {
                        foreach (TreeNode tn in e.Node.Nodes)
                        {
                            if (tn.Checked != e.Node.Checked)
                            {
                                tn.Checked = e.Node.Checked;
                            }
                        }
                    }
                }
                if (e.Node.Parent != null && e.Node.Parent is TreeNode)
                {
                    bool ParentNode = true;
                    foreach (TreeNode tn in e.Node.Parent.Nodes)
                    {
                        if (tn.Checked == false)
                        {
                            ParentNode = false;
                        }
                    }
                    if (e.Node.Parent.Checked != ParentNode && (e.Node.Checked == false || e.Node.Checked == true && e.Node.Parent.Checked == false))
                    {
                        e.Node.Parent.Checked = ParentNode;
                    }
                }
            }
            catch (Exception ex)
            {
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            templateFileNames.Clear();
            RecursiveTree(this.tvTemplate);
            MessageBox.Show(string.Join(",", templateFileNames));
        }

        ///递归TreeView，把所有被选中的节点ID记录到ArrayList中
        private void RecursiveTree(TreeView tree)
        {
            TreeNodeCollection nodes = tree.Nodes;
            foreach (TreeNode n in nodes)
            {
                RecursiveNodes(n);
                AddCheckedID(n);
            }
        }

        ///递归TreeView的节点，把所有被选中的节点ID记录到ArrayList中
        private void RecursiveNodes(TreeNode node)
        {
            foreach (TreeNode tn in node.Nodes)
            {
                RecursiveNodes(tn);
                AddCheckedID(tn);
            }
        }

        ///把所有被选中的节点ID和记录到ArrayList中
        private void AddCheckedID(TreeNode node)
        {
            if (node.Checked)
            {
                if (node.Tag is FileInfo)
                {
                    FileInfo fi = node.Tag as FileInfo;
                    templateFileNames.Add(fi.FullName);
                }
            }
        }
    }
}